package org.ecpkn.PAGridSim.core;

import gridsim.AllocPolicy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ecpkn.PAGridSim.core.configuration.Configuration;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachine;
import org.ecpkn.PAGridSim.core.offlineOptimization.ChangingWorkloadOptimizationAlgo;
import org.ecpkn.PAGridSim.core.tasksCreation.PeriodTaskCreator;
import org.ecpkn.PAGridSim.core.tasksCreation.RandomTaskCreator;
import org.ecpkn.PAGridSim.core.tasksCreation.Task;
import org.reflections.Reflections;

/**
 * Class that adopts "Factory" design partner. Actually it supports 4 entities'
 * creation dynamically. They are:
 * TaskCreator,PAMachine,PAPolicy,ChangingWorkloadOptimizationAlgo
 * 
 * @author Lin Zhou
 * 
 */

@SuppressWarnings("unchecked")
public class PAEntityFactory {

	private static final Logger logger = LogManager
			.getLogger(PAEntityFactory.class);

	public static final class Const {
		// Names
		public static final String EMPTY = "";
		public static final String TASK_CREATOR = "TaskCreator";
		public static final String PAMACHINE = "PAMachine";
		public static final String PAPOLICIES = "PAPolicy";
		public static final String OPTIMIZATION_ALGO = "ChangingWorkloadOptimizationAlgo";
	}

	private static Set<Class<? extends PeriodTaskCreator>> TASKCREATOR_CLASS;
	private static Set<Class<? extends PAMachine>> PAMACHINE_CLASS;
	private static Set<Class<? extends AllocPolicy>> PAPOLICY_CLASS;
	private static Set<Class<? extends ChangingWorkloadOptimizationAlgo>> CHANGINGWORKLOADOPTIMIZATION_CLASS;
	private static Reflections reflections;

	static {

		reflections = new Reflections(Configuration.getInstance()
				.getParameterString(
						Configuration.Const.CUSTOMIZED_ENTITY_PACKAGE));
		PAPOLICY_CLASS = (Set<Class<? extends AllocPolicy>>) initActualPossibleEntities(
				PAPOLICY_CLASS, AllocPolicy.class, Const.PAPOLICIES);

		CHANGINGWORKLOADOPTIMIZATION_CLASS = (Set<Class<? extends ChangingWorkloadOptimizationAlgo>>) initActualPossibleEntities(
				CHANGINGWORKLOADOPTIMIZATION_CLASS,
				ChangingWorkloadOptimizationAlgo.class, Const.OPTIMIZATION_ALGO);

		PAMACHINE_CLASS = (Set<Class<? extends PAMachine>>) initActualPossibleEntities(
				PAMACHINE_CLASS, PAMachine.class, Const.PAMACHINE);

		TASKCREATOR_CLASS = (Set<Class<? extends PeriodTaskCreator>>) initActualPossibleEntities(
				TASKCREATOR_CLASS, PeriodTaskCreator.class, Const.TASK_CREATOR);
	}

	public static AllocPolicy createCustomizedPolicy(String policyName,
			String resourceName, String policyId) throws Exception {
		if (policyName == null || Const.EMPTY.equals(policyName)) {
			logger.error("PAEntityFactory.createCustomizedPolicy:PolicyName can't not be null or empty. Please check your configuration file.");
		}
		AllocPolicy paPolicy = null;
		Iterator<Class<? extends AllocPolicy>> iterator = PAPOLICY_CLASS
				.iterator();
		while (iterator.hasNext()) {
			Class<?> c = iterator.next();
			if (c.getSimpleName().equalsIgnoreCase(policyName)) {
				try {
					paPolicy = (AllocPolicy) c.getConstructor(String.class,
							String.class).newInstance(resourceName, policyId);
				} catch (Exception ex) {
					logger.error("PAEntityFactory.createCustomizedPolicy: Cannot create instance for "
							+ policyName);
				}
				break;
			}
		}
		if (paPolicy == null) {
			String errorInfo = "PAEntityFactory.createCustomizedPolicy: policyName not found/ "
					+ policyName;
			logger.error(errorInfo);
			throw new Exception(errorInfo);
		}
		return paPolicy;
	}

	public static ChangingWorkloadOptimizationAlgo createCustomizedOptimizationMethod(
			String optimizationName, ArrayList<Task> tasks) throws Exception {
		if (optimizationName == null || Const.EMPTY.equals(optimizationName)) {
			logger.error("PAEntityFactory.createCustomizedOptimizationMethod:OptimizationMethod can't not be null or empty. Please check your configuration file.");
		}

		ChangingWorkloadOptimizationAlgo method = null;
		Iterator<Class<? extends ChangingWorkloadOptimizationAlgo>> iterator = CHANGINGWORKLOADOPTIMIZATION_CLASS
				.iterator();
		while (iterator.hasNext()) {
			Class<?> c = iterator.next();
			if (c.getSimpleName().equalsIgnoreCase(optimizationName)) {
				try {
					method = (ChangingWorkloadOptimizationAlgo) c
							.getConstructor(ArrayList.class).newInstance(tasks);
				} catch (Exception ex) {
					logger.error("PAEntityFactory.createCustomizedOptimizationMethod: cannot create instance for "
							+ optimizationName);
				}
				break;
			}
		}
		if (method == null) {
			String errorInfo = "PAEntityFactory.createCustomizedOptimizationMethod: methodName not found/ "
					+ optimizationName;
			logger.error(errorInfo);
			throw new Exception(errorInfo);
		}
		return method;
	}

	public static PAMachine createCustomizedMachine(String machineName, int id,
			int numPE, int ratingPE) throws Exception {
		PAMachine m = null;
		if (machineName == null || Const.EMPTY.equals(machineName)) {
			machineName = "PAMachine";
			m = new PAMachine(id, numPE, ratingPE);
		} else {
			Iterator<Class<? extends PAMachine>> iterator = PAMACHINE_CLASS
					.iterator();
			while (iterator.hasNext()) {
				Class<?> c = iterator.next();
				if (c.getSimpleName().equalsIgnoreCase(machineName)) {
					try {
						m = (PAMachine) c.getConstructor(int.class, int.class,
								int.class).newInstance(id, numPE, ratingPE);
					} catch (Exception ex) {
						logger.error("PAEntityFactory.createCustomizedMachine: cannot create instance for "
								+ machineName);
					}
					break;
				}
			}
			if (m == null) {
				String errorInfo = "PAEntityFactory.createCustomizedMachine: machineName not found/ "
						+ machineName;
				logger.error(errorInfo);
				throw new Exception(errorInfo);
			}
		}
		return m;
	}

	public static PeriodTaskCreator createCustomizedTaskCreator(
			String taskCreatorName) throws Exception {
		PeriodTaskCreator tc = null;
		if (taskCreatorName == null || Const.EMPTY.equals(taskCreatorName)) {
			taskCreatorName = "RandomTaskCreator";
			tc = new RandomTaskCreator();
		} else {
			Iterator<Class<? extends PeriodTaskCreator>> iterator = TASKCREATOR_CLASS
					.iterator();
			while (iterator.hasNext()) {
				Class<?> c = iterator.next();
				if (c.getSimpleName().equalsIgnoreCase(taskCreatorName)) {
					try {
						tc = (PeriodTaskCreator) c.getConstructor()
								.newInstance();
					} catch (Exception ex) {
						logger.error("PAEntityFactory.createCustomizedTaskCreator: cannot create instance for "
								+ taskCreatorName);
					}
					break;
				}
			}
			if (tc == null) {
				String errorInfo = "PAEntityFactory.createCustomizedTaskCreator: taskCreatorName not found/ "
						+ taskCreatorName;
				logger.error(errorInfo);
				throw new Exception(errorInfo);
			}
		}
		return tc;
	}

	private static Set<?> initActualPossibleEntities(Set<?> classSet,
			Class<?> c, String papolicies) {
		classSet = reflections.getSubTypesOf(c);
		classSet = Collections.unmodifiableSet(classSet);
		printActualPossibleEntities(classSet, papolicies);
		return classSet;
	}

	@SuppressWarnings("rawtypes")
	private static void printActualPossibleEntities(Set entitySet,
			String entityType) {
		logger.trace("PAEntityFactory : For the moment, there are "
				+ entitySet.size() + " " + entityType
				+ " that you can try. Some of them may be abstract entities");
		Iterator iterator = entitySet.iterator();
		StringBuilder infoMsg=new StringBuilder();
		infoMsg.append("They are:\n");
		while (iterator.hasNext()) {
			infoMsg.append(iterator.next().toString()+"\n");
		}
		logger.trace(infoMsg.toString());
	}
}
